// typescript 类型系统支持3种可以声明任意类型的变量 type infer 类型参数
// type 叫做类型别名
type ttt = Promise<number>;

// infer 用于类型的提取 然后存放到一个变量里 相当于局部变量
type GetValueType1<P> = P extends Promise<infer Value> ? Value : never;

// 类型参数用于接收具体的类型 在类型运算种也相当于局部变量
type isTwo<T> = T extends 2 ? true : false;

// 严格来说 也不叫 变量 因为他们都不可以重新赋值
// 不能修改如何产生新类型 那么就得用到 重新构造

/**
 * 重新构造
 * TypeScript 的 type、infer、类型参数声明的变量都不能修改，想对类型做各种变换产生新的类型就需要重新构造
 * 索引类型，也就是多个元素的聚合类型的重新构造复杂一些，涉及到了映射类型的语法
 */

// 数组 Push Unshift

type tuple = [1, 2, 3];

type Push<Arr extends unknown[], Ele> = [...unknown[], Ele];

type Unshift<Arr extends unknown[], Ele> = [Ele, ...unknown[]];

// Zip
type tuple1 = [1, 2];
type tuple2 = ['ge', 'peng'];

// 合并为这种
type tupleAns = [[1, 'ge'], [2, 'peng']];

// 实现
/**
 * 思路 One Other 是两个元组 [unknown, unknown] 代表任意两个类型的元素构成的元组
 * ifner 声明 OneFirst  OneSecond  OtherFirst OnterSecond
 * 分别 推导  One Other 两个参数 是否存在 存在 返回 [[OneFirst, OtherFirst], [OneSecond, OnterSecond]]
 * 缺陷是固定死2个任意类型
 */
type Zip<
  One extends [unknown, unknown],
  Other extends [unknown, unknown]
> = One extends [infer OneFirst, infer OneSecond]
  ? Other extends [infer OtherFirst, infer OnterSecond]
    ? [[OneFirst, OtherFirst], [OneSecond, OnterSecond]]
    : []
  : [];

type ZipRes = Zip<tuple1, tuple2>;

/**
 * 递归思路
 * One Other 是两个元组任意长度 任意类型的 元组
 * infer 声明 OneFirst OneRest OtherFirst OtherRest
 * 如果存在 把剩余值返回在 [OneFirst,OtherFirst,...Zip2<OneRest,OtherRest>]
 * 递归Zip2 把剩余的参数再次执行 推导过程 直到 匹配不出来 返回 []
 */
type Zip2<One extends unknown[], Other extends unknown> = One extends [
  infer OneFirst,
  ...infer OneRest
]
  ? Other extends [infer OtherFirst, ...infer OtherRest]
    ? [[OneFirst, OtherFirst], ...Zip2<OneRest, OtherRest>]
    : []
  : [];

type Zip2Res = Zip2<tuple1, tuple2>;

// 字符串类型的重新构造 想把peng 转为 首字母大写 Peng
/**
 * 通过模板字符 infer First 取出首个字母 提取后面的字符到局部变量Rest
 * 用内置高级类型Uppercase 转为大写
 */
type CapitalizeStr<Str extends String> =
  Str extends `${infer First}${infer Rest}`
    ? `${Uppercase<First>}${Rest}`
    : Str;

type CapitlizeStrRes = CapitalizeStr<'peng'>;

// 实现 dong_dong_dong 到 dongDongDong 的变换。
/**
 * 通过infer 声明Left Right Rest 用_ 分割模板字符 取出_后面的第一个字符 转为大写
 * Rest 再通过递归去依次处理
 */
type CamelCase<Str extends string> =
  Str extends `${infer Left}_${infer Right}${infer Rest}`
    ? `${Left}${Uppercase<Right>}${CamelCase<Rest>}`
    : Str;

type CamelCaseRes = CamelCase<'peng_peng_peng'>;

// 删除一个字段字符串
type DropSubStr<
  Str extends string,
  SubStr extends string
> = Str extends `${infer Prefix}${SubStr}${infer Suffix}`
  ? DropSubStr<`${Prefix}${Suffix}`, SubStr>
  : Str;

type DropSubStrRes = DropSubStr<'peng~~~', '~'>;

// 函数类型的重新构造
// 之前分别实现了参数和返回值的提取
// 现在已有的函数类型上添加一个参数
/**
 * 思路 args 用 infer Args 声明一个局部变量
 * 如果符合的情况下 我们需要 对Args解构 并把 Arg 传入在末尾 就像之前事项 push 类似
 */
type AppendArgument<Func extends Function, Arg> = Func extends (
  ...args: infer Args
) => infer ReturnType
  ? (...args: [...Args, Arg]) => ReturnType
  : never;

type AppendArgumentRes = AppendArgument<(name: string) => boolean, number>;

// 索引类型的重新构造
type obj1 = {
  readonly name: string; // 只读
  age?: number; // 可选
  gender: boolean;
};

// 对value 做映射 修改
type Mapping<Obj extends object> = {
  [K in keyof Obj]: [Obj[K], Obj[K]];
};

type MappingRes = Mapping<{ a?: 1 }>;

// 对key 做映射 修改 key 改为大写
type UppercaseKey<Obj extends object> = {
  [K in keyof Obj as Uppercase<K & string>]: Obj[K];
};

type UppercaseKeyRes = UppercaseKey<{ peng: 666 }>;

// Record ts 内置了高级类型来创建索引 下面是实现方式
type RecordTs<K extends string | number | symbol, T> = {
  [P in K]: T;
};

// 更加语义化的实现 UppercaseKey
type UppercaseKey1<Obj extends Record<string, any>> = {
  [K in keyof Obj as Uppercase<K & string>]: Obj[K];
};

// 添加 readonly 修改为只读
type ToReadonly<T> = {
  readonly [K in keyof T]: T[K];
};

type ToReadonlyRes = ToReadonly<{ name: 'peng'; age: 18 }>;

// 添加  ?  修改为可选
type toPartial<T> = {
  [K in keyof T]?: T[K];
};

type toPartialRes = toPartial<{ name: 'peng'; age: 18 }>;

// 去掉 readonly
type ToMutable<T> = {
  -readonly [K in keyof T]: T[K];
};

type ToMutableRes = ToMutable<ToReadonlyRes>; // 把前面的 修改为可读的传入

// 去掉 可选 ?
type ToRequired<T> = {
  [K in keyof T]-?: T[K];
};

type ToRequiredRes = ToRequired<toPartialRes>; // 把前面的 修改为可选的传入

// 在构造的新的索引类型的时候根据值的类型做下过滤
/**
 * 思路 用 as 重映射 取出 Obj[K] 是否和 当前约束的 ValueType 类型一样
 * 是的话 使用当前的K
 */
type FilterByValueType<Obj extends Record<string, any>, ValueType> = {
  [K in keyof Obj as Obj[K] extends ValueType ? K : never]: Obj[K];
};

interface Person1 {
  name: string;
  age: number;
  hobby: [];
}

type FilterByValueTypeRes = FilterByValueType<Person1, string | number>;

